﻿namespace PugLib.Collections
{
	using System;
	using System.Collections;
	using System.Collections.Generic;
	using System.Linq;

	/// <summary>
	/// Wrap dictionary with key-based validation.
	/// All data access is predicated by a validator delegate.
	/// </summary>
	/// <typeparam name="TKey">Key type</typeparam>
	/// <typeparam name="TValue">Value type</typeparam>
	public class ValidationDictionary<TKey, TValue> : IDictionary<TKey, TValue>
	{
		private readonly IDictionary<TKey, TValue> _dict;
		private readonly IList<TKey> _invalidatedKeys;
		private readonly Predicate<KeyValuePair<TKey, TValue>> _validator;

		public ValidationDictionary(Predicate<KeyValuePair<TKey, TValue>> validator)
		{
			_dict = new Dictionary<TKey, TValue>();
			_validator = validator;
			_invalidatedKeys = new List<TKey>();
		}

		#region IDictionary<TKey,TValue> Members
		public void Add(TKey key, TValue value)
		{
			_dict.Add(key, value);
		}

		public bool ContainsKey(TKey key)
		{
			bool flag = _dict.ContainsKey(key) && IsValid(new KeyValuePair<TKey, TValue>(key, _dict[key]));
			RemoveInvalid();
			return flag;
		}

		public ICollection<TKey> Keys
		{
			get
			{
				TKey[] flag = _dict.Where(IsValid).Select(pair => pair.Key).ToArray();
				RemoveInvalid();
				return flag;
			}
		}

		public bool Remove(TKey key)
		{
			if (_dict.ContainsKey(key))
			{
				if (IsValid(new KeyValuePair<TKey, TValue>(key, _dict[key])))
				{
					return _dict.Remove(key);
				}
				RemoveInvalid();
				return false;
			}
			return false;
		}

		public bool TryGetValue(TKey key, out TValue value)
		{
			try
			{
				value = _dict[key];
				bool flag = IsValid(new KeyValuePair<TKey, TValue>(key, value));
				RemoveInvalid();
				return flag;
			}
			catch (KeyNotFoundException)
			{
				value = default(TValue);
				return false;
			}
		}

		public ICollection<TValue> Values
		{
			get
			{
				TValue[] flag = _dict.Where(IsValid).Select(pair => pair.Value).ToArray();
				RemoveInvalid();
				return flag;
			}
		}

		public TValue this[TKey key]
		{
			get
			{
				TValue value = _dict[key];
				if (IsValid(new KeyValuePair<TKey, TValue>(key, value)))
				{
					return value;
				}
				RemoveInvalid();
				throw new KeyNotFoundException("Key is invalidated");
			}
			set { _dict[key] = value; }
		}

		public void Add(KeyValuePair<TKey, TValue> item)
		{
			_dict.Add(item);
		}

		public void Clear()
		{
			_dict.Clear();
		}

		public bool Contains(KeyValuePair<TKey, TValue> item)
		{
			bool flag = _dict.Contains(item) && IsValid(item);
			RemoveInvalid();
			return flag;
		}

		public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
		{
			foreach (KeyValuePair<TKey, TValue> pair in this)
			{
				array[arrayIndex++] = pair;
			}
		}

		public int Count
		{
			get
			{
				int flag = _dict.Where(IsValid).Count();
				RemoveInvalid();
				return flag;
			}
		}

		public bool IsReadOnly
		{
			get { return _dict.IsReadOnly; }
		}

		public bool Remove(KeyValuePair<TKey, TValue> item)
		{
			bool isValid = _dict.ContainsKey(item.Key) && IsValid(item);
			return _dict.Remove(item) && isValid;
		}

		public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
		{
			foreach (KeyValuePair<TKey, TValue> pair in _dict.Where(IsValid))
			{
				yield return pair;
			}
			RemoveInvalid();
			yield break;
		}

		IEnumerator IEnumerable.GetEnumerator()
		{
			return GetEnumerator();
		}
		#endregion

		/// <summary>
		/// Invoke validator.  Mark invalidated keys for removal.
		/// </summary>
		/// <param name="pair"></param>
		/// <returns></returns>
		private bool IsValid(KeyValuePair<TKey, TValue> pair)
		{
			bool isValid = _validator(pair);
			if (!isValid)
			{
				_invalidatedKeys.Add(pair.Key);
			}
			return isValid;
		}

		/// <summary>
		/// Remove elements marked for removal by IsValid().
		/// </summary>
		private void RemoveInvalid()
		{
			foreach (TKey key in _invalidatedKeys)
			{
				_dict.Remove(key);
			}
			_invalidatedKeys.Clear();
		}
	}
}